/**
 * File: util.h
 * Created Data: 2006/8/23
 * Author: Kimi Pu
 * Descirption: This file contains macro and define for facility of implement.	
 */
#ifndef _UTIL_H_
#define _UTIL_H_

#ifndef _linux_
#include <string>
#include <windows.h>
#include <tchar.h>
#include "data_define/type_define.h"
#endif

#ifdef _linux_
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <stdio.h>
#endif

#ifndef SAFE_DELETE
#define SAFE_DELETE(x) if((x))delete[] (x); (x) = NULL;
#endif

#ifndef SAFE_DELETE_OBJ
#define SAFE_DELETE_OBJ(x) if(x)delete (x); (x) = NULL;
#endif

#define CHECK_PTR(x) assert(x)

class tMutex
{
protected:
#ifdef _linux_
    pthread_mutex_t mutex;
#else
	HANDLE mutex;
#endif
public:
    tMutex()
    {
#ifdef _linux_
		pthread_mutexattr_t mutexattr;
		pthread_mutexattr_init(&mutexattr);
		// TOTO: set the mutex attributes.
		pthread_mutexattr_settype(&mutexattr, 
					     PTHREAD_MUTEX_RECURSIVE_NP);
		if (pthread_mutex_init(&mutex, &mutexattr) != 0)
#else 
		if ((mutex = CreateMutex(NULL, FALSE, NULL)) == NULL)
#endif
		    printf("Internal error: pthread_mutex_init error!.\n");
#ifdef _linux_
		pthread_mutexattr_destroy(&mutexattr);
#endif
    }

    ~tMutex()
    {
#ifdef _linux_
		pthread_mutex_destroy(&mutex);
#else
		CloseHandle(mutex);
#endif
    }

    void Wait()
    {
#ifdef _linux_
		pthread_mutex_lock(&mutex);
#else
		WaitForSingleObject(mutex, INFINITE);
#endif
    }

    void Release()
    {
#ifdef _linux_
		pthread_mutex_unlock(&mutex);
#else
		ReleaseMutex(mutex);
#endif
    }
};

class tSemaphore 
{
protected:
#ifdef _linux_
    sem_t sem;
#else
	HANDLE sem;
#endif
    unsigned int count;
public:
    tSemaphore()
    {
#ifdef _linux_
	int r = sem_init(&sem, 0, 0);
	if (r != 0)
#else
	if ((sem = CreateSemaphore(NULL, 0, 65535, NULL)) == NULL)
#endif
	    printf("Internal Error: can not create semaphore.\n");

	count = 0;
    }

    ~tSemaphore()
    {
#ifdef _linux_
	sem_destroy(&sem);
#else
		CloseHandle(sem);
#endif
    }

    int Wait()
    {
#ifdef _linux_
		int r = 0;
		while (1)
		{
			r = sem_wait(&sem);
			if (r != 0 && errno == EINTR)
			{
			//printf("Semaphore is waked by interupt.\n");
			//printf("Continue to wait.\n");
			continue;
			}
			break;
		}
		return r;
#else
		WaitForSingleObject(sem, INFINITE);
		return 0;
#endif
    }

    int WaitTime(unsigned int timeout)
	{
#ifdef _linux_
	    int r = 0;
	    while (1)
	    {
			time_t tt = time(NULL);
			timespec ts;
			ts.tv_sec = tt + timeout/1000;
			ts.tv_nsec = (timeout%1000)*1000000;
			r = sem_timedwait(&sem, &ts);
			if (r != 0 && errno == EINTR)
			{
			    continue;
			}
			else if (r != 0 && errno == ETIMEDOUT)
			{
			    return 1;
			}
			else
			    return 0;
	    }
#else
		if (WaitForSingleObject(sem, timeout) == WAIT_TIMEOUT)
			return 1;
		else
			return 0;
#endif
	}

    int Try()
    {
#ifdef _linux_
	int r = sem_trywait(&sem);
	if (r == 0)
	    return 0;
	else if (errno = EAGAIN)
	    return -1;
	else
	    return -2;
#else
		//Windows don't support this operation.
		return -2;
#endif
    }

    void Post()
    {
#ifdef _linux_
	sem_post(&sem);
#else
		ReleaseSemaphore(sem, 1, NULL);
#endif
    }
};

#ifdef _linux_
class tEvent
{
protected:
	pthread_mutex_t mutex;
	sem_t single_sem;
	bool b_man_reset;
	unsigned int c;
public:
	tEvent(bool bManualReset=false)
	{
		// init the semaphore
		sem_init(&single_sem, 0, 0);
		b_man_reset = bManualReset;
		pthread_mutex_init(&mutex, NULL);
		c = 0;
	}
	~tEvent()
	{
		sem_destroy(&single_sem);
		pthread_mutex_destroy(&mutex);
	}
	void Wait()
	{
		while (1)
		{
			int r = sem_wait(&single_sem);
			if (r != 0 && errno == EINTR)
			{
				printf("Semaphore is waked by interupt.\n");
				printf("Continue to wait.\n");
				continue;
			}
			break;
		}

		if (b_man_reset)
			sem_post(&single_sem);
	}
	void SetEvent()
	{
		pthread_mutex_lock(&mutex);
		if (c == 0)
		{
			sem_post(&single_sem);
			//printf("Setevent.\n");
			c++;
		}
		else
			printf("!!!!!!!!!!!!!!!!!!!!!!SetEvent.\n");
		pthread_mutex_unlock(&mutex);
	}
	void ResetEvent()
	{
		pthread_mutex_lock(&mutex);
		for (; c > 0; c--)
			sem_wait(&single_sem);
		if (c != 0)
			printf("!!!!!!!!!!!!!!!!!!!!!!!!ResetEvent\n");
		pthread_mutex_unlock(&mutex);
	}
};
#endif

class AUTO_MUTEX_2
{
	tMutex *mutex;
public:
	AUTO_MUTEX_2(tMutex &m)
	{
		mutex = &m;
		mutex->Wait();
	}
	
	~AUTO_MUTEX_2()
	{
		mutex->Release();
	}
};

#ifndef _linux_
class AUTO_MUTEX
{
	bool m_bLocked;
	HANDLE mutex;
	DWORD WaitRes;
public:
	AUTO_MUTEX(HANDLE m)
	{
		m_bLocked = false;
		mutex = m;
		WaitRes = WaitForSingleObject(mutex, INFINITE);
		m_bLocked = true;
	}
	AUTO_MUTEX(HANDLE m, DWORD TimeOut)
	{
		m_bLocked = false;
		mutex = m;
		WaitRes = WaitForSingleObject(mutex, TimeOut);
		m_bLocked = true;
	}
	~AUTO_MUTEX()
	{
		if (m_bLocked)
			ReleaseMutex(mutex);
	}

	DWORD GetWaitResult()
	{
		return WaitRes;
	}

	void Release()
	{
		if (m_bLocked)
		{
			m_bLocked = false;
			ReleaseMutex(mutex);
		}
	}

	DWORD Lock(DWORD timeout)
	{
		DWORD r = WaitForSingleObject(mutex, timeout);
		if (r == WAIT_OBJECT_0)
		{
			m_bLocked = true;
		}
		return r;
	}
};
#endif

#ifndef _linux_
void DumpToFile(char * str);
#endif


inline void Exchange(char * a, char * b)
{
	*a ^= *b;
	*b ^= *a;
	*a ^= *b;
}

inline void ReverseByte(char * buf, unsigned int len)
{
	char * tail = buf+len-1;
	len >>= 1;
	for (unsigned i = 0; i < len ; i++)
	{
		Exchange(buf+i, tail-i);
	}
}

template<class T> inline void ChangeEndian(T *px)
{
	ReverseByte((char*)px, sizeof(px)); 
}

#ifndef _linux_
std::string WideCharToMultiChar(std::wstring &widestr);
std::wstring MultiCharToWideChar(std::string &str);
std::wstring GetModulePath();
std::wstring GetModuleBaseName();
std::wstring GetTempFile(const std::wstring &subfolder);
std::wstring CreateTempFolder(const std::wstring & subfolder);
bool CreateFolder(std::wstring folder);
bool DeleteFolderTree(std::wstring folder, std::wstring stopfolder); 
std::string WideCharToUTF8(std::wstring &widestr);
std::wstring UTF8ToWideChar(std::string &str);

#endif

#ifdef _linux_

#ifndef _UNICODE 
#define tprintf		printf
#else
#define tprintf		wprintf
#endif // _UNICODE

#endif // _linux_

#ifndef _linux_		
// For Windows
#define DBGSTR(x)\
{\
	OutputDebugString(_T(x));\
}

#define DBGSTR1(x,v) \
{\
	TCHAR debugstr[1024];\
	_stprintf_s(debugstr, 1024, _T(x), v);\
	OutputDebugString(debugstr);\
}

#define DBGSTR2(x, v1, v2)\
{\
	TCHAR debugstr[1024];\
	_stprintf_s(debugstr, 1024, _T(x), v1, v2);\
	OutputDebugString(debugstr);\
}

#define DBGSTR3(x, v1, v2, v3)\
{\
	TCHAR debugstr[1024];\
	_stprintf_s(debugstr, 1024, _T(x), v1, v2, v3);\
	OutputDebugString(debugstr);\
}

#define DBGSTR4(x, v1, v2, v3, v4)\
{\
	TCHAR debugstr[1024];\
	_stprintf_s(debugstr, 1024, _T(x), v1, v2, v3, v4);\
	OutputDebugString(debugstr);\
}

#define DBGSTR5(x, v1, v2, v3, v4, v5)\
{\
	TCHAR debugstr[1024];\
	_stprintf_s(debugstr, _T(x), 1024, v1, v2, v3, v4, v5);\
	OutputDebugString(debugstr);\
}

#define DBGSTRA(x)\
{\
	OutputDebugStringA(x);\
}

#define DBGSTR1A(x,v)\
{\
	char debugstr[1024];\
	sprintf_s(debugstr, x, v);\
	OutputDebugStringA(debugstr);\
}

#define DBGSTR2A(x, v1, v2)\
{\
	char debugstr[1024];\
	sprintf_s(debugstr, x, v1, v2);\
	OutputDebugStringA(debugstr);\
}

#define DBGSTR3A(x, v1, v2, v3)\
{\
	char debugstr[1024];\
	sprintf_s(debugstr, x, v1, v2, v3);\
	OutputDebugStringA(debugstr); \
}

#define DBGSTR4A(x, v1, v2, v3, v4)\
{\
	char debugstr[1024];\
	sprintf_s(debugstr, x, v1, v2, v3, v4);\
	OutputDebugStringA(debugstr);\
}

#define DBGSTR5A(x, v1, v2, v3, v4, v5)\
{\
	char debugstr[1024];\
	sprintf_s(debugstr, x, v1, v2, v3, v4, v5);\
	OutputDebugStringA(debugstr);\
}
#else
// For Linux
#define DBGSTR(x)\
{\
	tprintf(x);\
}

#define DBGSTR1(x,v) \
{\
	tprintf(x, v);\
}

#define DBGSTR2(x, v1, v2) \
{\
	tprintf(x, v1, v2);\
}

#define DBGSTR3(x, v1, v2, v3)\
{\
	tprintf(x, v1, v2, v3);\
}

#define DBGSTR4(x, v1, v2, v3, v4)\
{\
	tprintf(x, v1, v2, v3, v4);\
}

#define DBGSTR5(x, v1, v2, v3, v4, v5)\
{\
	tprintf(x, v1, v2, v3, v4, v5);\
}

#define DBGSTRA(x)\
{\
	printf(x);\
}

#define DBGSTR1A(x,v) \
{\
	printf(x, v);\
}

#define DBGSTR2A(x, v1, v2)\
{\
	printf(x, v1, v2);\
}

#define DBGSTR3A(x, v1, v2, v3)\
{\
	printf(x, v1, v2, v3);\
}

#define DBGSTR4A(x, v1, v2, v3, v4)\
{\
	printf(x, v1, v2, v3, v4);\
}

#define DBGSTR5A(x, v1, v2, v3, v4, v5)\
{\
	printf(x, v1, v2, v3, v4, v5);\
}
#endif	// _linux_

#endif	// _UTIL_H


